/*
 * contrib/citext/citext.c
 */
#include "postgres.h"

#include "catalog/pg_collation.h"
#include "common/hashfn.h"
#include "utils/builtins.h"
#include "utils/formatting.h"
#include "utils/varlena.h"

PG_MODULE_MAGIC;

/*
 *		====================
 *		前向声明
 *		====================
 */

static int32 fc_citextcmp(text *fc_left, text *fc_right, Oid fc_collid);
static int32 fc_internal_citext_pattern_cmp(text *fc_left, text *fc_right, Oid fc_collid);

/*
 *		=================
 *		工具函数
 *		=================
 */

/*
 * citextcmp()
 * citext字符串的内部比较函数。
 * 返回int32负、零或正。
 */
static int32 fc_citextcmp(text *fc_left, text *fc_right, Oid fc_collid)
{
	char	   *fc_lcstr,
			   *fc_rcstr;
	int32		fc_result;

	/*
	 * 我们必须使用DEFAULT_COLLATION_OID进行str_tolower调用，而不是
	 * 输入的排序规则，这是您可能期望的。这是为了确保
	 * citext的相等性和哈希函数的行为与排序规则无关。我们
	 * 应该在核心基础设施能够处理
	 * 与排序规则相关的相等性和哈希函数后进行更改。
	 */

	fc_lcstr = str_tolower(VARDATA_ANY(fc_left), VARSIZE_ANY_EXHDR(fc_left), DEFAULT_COLLATION_OID);
	fc_rcstr = str_tolower(VARDATA_ANY(fc_right), VARSIZE_ANY_EXHDR(fc_right), DEFAULT_COLLATION_OID);

	fc_result = varstr_cmp(fc_lcstr, strlen(fc_lcstr),
						fc_rcstr, strlen(fc_rcstr),
						fc_collid);

	pfree(fc_lcstr);
	pfree(fc_rcstr);

	return fc_result;
}

/*
 * citext_pattern_cmp()
 * citext字符串的内部逐字符比较函数。
 * 返回int32负、零或正。
 */
static int32 fc_internal_citext_pattern_cmp(text *fc_left, text *fc_right, Oid fc_collid)
{
	char	   *fc_lcstr,
			   *fc_rcstr;
	int			fc_llen,
				fc_rlen;
	int32		fc_result;

	fc_lcstr = str_tolower(VARDATA_ANY(fc_left), VARSIZE_ANY_EXHDR(fc_left), DEFAULT_COLLATION_OID);
	fc_rcstr = str_tolower(VARDATA_ANY(fc_right), VARSIZE_ANY_EXHDR(fc_right), DEFAULT_COLLATION_OID);

	fc_llen = strlen(fc_lcstr);
	fc_rlen = strlen(fc_rcstr);

	fc_result = memcmp((void *) fc_lcstr, (void *) fc_rcstr, Min(fc_llen, fc_rlen));
	if (fc_result == 0)
	{
		if (fc_llen < fc_rlen)
			fc_result = -1;
		else if (fc_llen > fc_rlen)
			fc_result = 1;
	}

	pfree(fc_lcstr);
	pfree(fc_rcstr);

	return fc_result;
}

/*
 *		==================
 *		索引函数
 *		==================
 */

PG_FUNCTION_INFO_V1(citext_cmp);

Datum citext_cmp(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	int32		fc_result;

	fc_result = fc_citextcmp(fc_left, fc_right, PG_GET_COLLATION());

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_INT32(fc_result);
}

PG_FUNCTION_INFO_V1(citext_pattern_cmp);

Datum citext_pattern_cmp(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	int32		fc_result;

	fc_result = fc_internal_citext_pattern_cmp(fc_left, fc_right, PG_GET_COLLATION());

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_INT32(fc_result);
}

PG_FUNCTION_INFO_V1(citext_hash);

Datum citext_hash(PG_FUNCTION_ARGS)
{
	text	   *fc_txt = PG_GETARG_TEXT_PP(0);
	char	   *fc_str;
	Datum		fc_result;

	fc_str = str_tolower(VARDATA_ANY(fc_txt), VARSIZE_ANY_EXHDR(fc_txt), DEFAULT_COLLATION_OID);
	fc_result = hash_any((unsigned char *) fc_str, strlen(fc_str));
	pfree(fc_str);

	/* 避免对烤制输入泄露内存 */
	PG_FREE_IF_COPY(fc_txt, 0);

	PG_RETURN_DATUM(fc_result);
}

PG_FUNCTION_INFO_V1(citext_hash_extended);

Datum citext_hash_extended(PG_FUNCTION_ARGS)
{
	text	   *fc_txt = PG_GETARG_TEXT_PP(0);
	uint64		fc_seed = PG_GETARG_INT64(1);
	char	   *fc_str;
	Datum		fc_result;

	fc_str = str_tolower(VARDATA_ANY(fc_txt), VARSIZE_ANY_EXHDR(fc_txt), DEFAULT_COLLATION_OID);
	fc_result = hash_any_extended((unsigned char *) fc_str, strlen(fc_str), fc_seed);
	pfree(fc_str);

	/* 避免对烤制输入泄露内存 */
	PG_FREE_IF_COPY(fc_txt, 0);

	PG_RETURN_DATUM(fc_result);
}

/*
 *		==================
 *		运算符函数
 *		==================
 */

PG_FUNCTION_INFO_V1(citext_eq);

Datum citext_eq(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	char	   *fc_lcstr,
			   *fc_rcstr;
	bool		fc_result;

	/* 我们无法在小写转换之前比较长度... */

	fc_lcstr = str_tolower(VARDATA_ANY(fc_left), VARSIZE_ANY_EXHDR(fc_left), DEFAULT_COLLATION_OID);
	fc_rcstr = str_tolower(VARDATA_ANY(fc_right), VARSIZE_ANY_EXHDR(fc_right), DEFAULT_COLLATION_OID);

	/*
	 * 由于我们只关心相等性或不相等性，我们可以避免在这里使用strcoll()的所有
	 * 费用，而只需进行按位比较。
	 */
	fc_result = (strcmp(fc_lcstr, fc_rcstr) == 0);

	pfree(fc_lcstr);
	pfree(fc_rcstr);
	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_ne);

Datum citext_ne(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	char	   *fc_lcstr,
			   *fc_rcstr;
	bool		fc_result;

	/* 我们无法在小写转换之前比较长度... */

	fc_lcstr = str_tolower(VARDATA_ANY(fc_left), VARSIZE_ANY_EXHDR(fc_left), DEFAULT_COLLATION_OID);
	fc_rcstr = str_tolower(VARDATA_ANY(fc_right), VARSIZE_ANY_EXHDR(fc_right), DEFAULT_COLLATION_OID);

	/*
	 * 由于我们只关心相等性或不相等性，我们可以避免在这里使用strcoll()的所有
	 * 费用，而只需进行按位比较。
	 */
	fc_result = (strcmp(fc_lcstr, fc_rcstr) != 0);

	pfree(fc_lcstr);
	pfree(fc_rcstr);
	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_lt);

Datum citext_lt(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_citextcmp(fc_left, fc_right, PG_GET_COLLATION()) < 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_le);

Datum citext_le(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_citextcmp(fc_left, fc_right, PG_GET_COLLATION()) <= 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_gt);

Datum citext_gt(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_citextcmp(fc_left, fc_right, PG_GET_COLLATION()) > 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_ge);

Datum citext_ge(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_citextcmp(fc_left, fc_right, PG_GET_COLLATION()) >= 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_pattern_lt);

Datum citext_pattern_lt(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_internal_citext_pattern_cmp(fc_left, fc_right, PG_GET_COLLATION()) < 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_pattern_le);

Datum citext_pattern_le(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_internal_citext_pattern_cmp(fc_left, fc_right, PG_GET_COLLATION()) <= 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_pattern_gt);

Datum citext_pattern_gt(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_internal_citext_pattern_cmp(fc_left, fc_right, PG_GET_COLLATION()) > 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

PG_FUNCTION_INFO_V1(citext_pattern_ge);

Datum citext_pattern_ge(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	bool		fc_result;

	fc_result = fc_internal_citext_pattern_cmp(fc_left, fc_right, PG_GET_COLLATION()) >= 0;

	PG_FREE_IF_COPY(fc_left, 0);
	PG_FREE_IF_COPY(fc_right, 1);

	PG_RETURN_BOOL(fc_result);
}

/*
 *		===================
 *		聚合函数
 *		===================
 */

PG_FUNCTION_INFO_V1(citext_smaller);

Datum citext_smaller(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	text	   *fc_result;

	fc_result = fc_citextcmp(fc_left, fc_right, PG_GET_COLLATION()) < 0 ? fc_left : fc_right;
	PG_RETURN_TEXT_P(fc_result);
}

PG_FUNCTION_INFO_V1(citext_larger);

Datum citext_larger(PG_FUNCTION_ARGS)
{
	text	   *fc_left = PG_GETARG_TEXT_PP(0);
	text	   *fc_right = PG_GETARG_TEXT_PP(1);
	text	   *fc_result;

	fc_result = fc_citextcmp(fc_left, fc_right, PG_GET_COLLATION()) > 0 ? fc_left : fc_right;
	PG_RETURN_TEXT_P(fc_result);
}
